#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
#include <string.h>
#include "threadpool.h"
#include <assert.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>

#define MAJOR_VERSION (1)
#define MINOR_VERSION (0)
#define REPAIR_VERSION (0)

#define VERSION (MAJOR_VERSION << 24 | MINOR_VERSION << 16 | REPAIR_VERSION)

#define thprint printf

#ifdef DEBUG
#define PRT(fmt, ...)                                                                            \
    do                                                                                           \
    {                                                                                            \
        fprintf(stdout, "[%s:%04d <%s>] " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
    } while (0)

#define DEB(fmt, ...)                                                                                 \
    do                                                                                                \
    {                                                                                                 \
        fprintf(stdout, "[DEB][%s:%04d <%s>] " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
    } while (0)
#else
#define PRT(fmt, ...) ;
#define DEB(fmt, ...) ;
#endif

#define ERR(fmt, ...)                                                                                 \
    do                                                                                                \
    {                                                                                                 \
        fprintf(stderr, "[ERR][%s:%04d <%s>] " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
    } while (0)
#ifdef CLOSE_INFO_PRT
#define POK(fmt, ...) ;
#else
#define POK(fmt, ...)                                                                            \
    do                                                                                           \
    {                                                                                            \
        fprintf(stderr, "[%s:%04d <%s>] " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
    } while (0)
#endif

#define TASK_QUEUE_MAX_SIZE (TASK_MAX_SIZE + 1) ///< 队列实际大小，牺牲一个存储空间

typedef struct Task
{
    unsigned int id;        ///< 任务 ID
    TASK_PRIORITY priority; ///< 任务优先级
    THRD_FUNC func;         ///< 任务函数
    void *arg;              ///< 任务函数参数
} TASK;

///< 任务队列数据结构
typedef struct TaskQueue
{
    TASK task[TASK_QUEUE_MAX_SIZE]; // 24*TASK_QUEUE_MAX_SIZE
    int head;                       // 4
    int rear;                       // 4
} TASKQ;                            // 8 + 24*n

typedef struct Thread
{
    unsigned long int id;
    unsigned long int cnt;
    pthread_t threads;
} THREAD; // 16

typedef struct ThreadPool
{
    atomic_int state;       // 4 线程池状态
    atomic_int task_count;  // 4 任务数量
    unsigned long int size; // 8 线程池大小
    pthread_cond_t cond;    // 48 条件变量
    pthread_mutex_t mutex;  // 40 互斥锁
    TASKQ taskQ;            // 8 + 24*TASK_QUEUE_MAX_SIZE
    THREAD thread[0];
} THREAD_POOL; // 576+16*n

typedef struct THRD_
{
    THREAD_POOL *pool;
    unsigned long int thid;
} THRD;

int is_empty(TASKQ *q)
{
    if (NULL == q)
    {
        return 0;
    }

    return (q->head == q->rear);
}

int is_full(TASKQ *q)
{
    if (NULL == q)
    {
        return 0;
    }

    return ((q->rear + 1) % TASK_QUEUE_MAX_SIZE == q->head);
}

int push(TASKQ *q, THRD_FUNC func, void *arg, int id)
{
    if (NULL == q || NULL == arg)
    {
        return -1;
    }

    if (is_full(q))
    {
        ERR("is_full\n");
        return -1;
    }
    DEB("push id[%d], h[%d], r[%d]\n", id, q->head, q->rear);
    q->task[q->rear].func = func;
    q->task[q->rear].arg = arg;
    q->task[q->rear].id = id;
    q->task[q->rear].priority = 0;
    q->rear++;
    q->rear %= TASK_QUEUE_MAX_SIZE;
    DEB("push id[%d], h[%d], r[%d]\n", id, q->head, q->rear);
    return 0;
}

TASK *pop(TASKQ *q)
{
    if (NULL == q)
    {
        return NULL;
    }

    if (is_empty(q))
    {
        ERR("is_empty\n");
        return NULL;
    }
    TASK *task = &q->task[q->head];
    DEB("pop id[%d], h[%d], r[%d]\n", task->id, q->head, q->rear);
    q->head++;
    q->head %= TASK_QUEUE_MAX_SIZE;
    DEB("pop id[%d], h[%d], r[%d]\n", task->id, q->head, q->rear);
    return (task);
}

void print_time()
{
    struct tm *t;
    time_t tt;
    time(&tt);
    t = localtime(&tt);
    thprint("[%02d-%02d %02d:%02d:%02d] ", t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
}

void print_time_mil()
{
    time_t time_sec = 0; // 秒
    long time_mil = 0;   // 1毫秒 = 1秒/1000
    struct timeval time_now = {0};
    struct tm *t;

    gettimeofday(&time_now, NULL);
    time_sec = time_now.tv_sec;
    time_mil = time_now.tv_usec / 1000;
    t = localtime(&time_sec);
    thprint("[%02d-%02d %02d:%02d:%02d.%03ld] ", t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, time_mil);
}

time_t get_timeval_ms()
{
    struct timeval time_now = {0};
    time_t time_sec = 0; // 秒
    time_t time_mil = 0; // 1毫秒 = 1秒/1000
    // time_t time_mic = 0; // 1微秒 = 1毫秒/1000

    gettimeofday(&time_now, NULL);
    time_sec = time_now.tv_sec;
    time_mil = time_sec * 1000 + time_now.tv_usec / 1000;
    // time_mic = time_now.tv_sec * 1000 * 1000 + time_now.tv_usec;
    return time_mil;
}

void *thread_pool_worker(void *arg)
{
    if (NULL == arg)
    {
        return NULL;
    }

    THRD thrd;
    memset(&thrd, 0, sizeof(THRD));
    memcpy(&thrd, (THRD *)arg, sizeof(THRD));
    unsigned long int id = thrd.thid;
    THREAD_POOL *pool = thrd.pool;

    char thread_name[16];
    snprintf(thread_name, sizeof(thread_name), "thrdworker_%02ld", id);
    (void)prctl(PR_SET_NAME, thread_name);

    PRT("Thread Worker [%02ld]\n", id);
    while (1)
    {
        pthread_mutex_lock(&pool->mutex);

        while (0 == pool->task_count && pool->state)
        {
            pthread_cond_wait(&pool->cond, &pool->mutex);
        }

        if (0 == pool->state && pool->task_count <= 0)
        {
            pthread_mutex_unlock(&pool->mutex);
            break;
        }
        pool->task_count--;
        TASK *task = pop(&pool->taskQ);
        pool->thread[id].cnt++;

        pthread_mutex_unlock(&pool->mutex);
        if (NULL == task || NULL == task->func)
        {
            continue;
        }
        print_time();
        thprint("Thread[%02ld] exec task[%04d]\n", pool->thread[id].id, task->id);
        task->func(task->arg);
    }
    return NULL;
}

THREAD_POOL *thread_pool_init(unsigned int size)
{
    THRD *thrd = (THRD *)malloc(sizeof(THRD) * size);
    if (NULL == thrd)
    {
        return NULL;
    }

    THREAD_POOL *pool = (THREAD_POOL *)malloc(sizeof(THREAD_POOL) + sizeof(THREAD) * size);
    if (NULL == pool)
    {
        free(thrd);
        return NULL;
    }

    memset(pool, 0, sizeof(THREAD_POOL) + sizeof(THREAD) * size);
    pool->size = size;
    pool->state = 1;
    pool->task_count = 0;
    pthread_cond_init(&pool->cond, NULL);
    pthread_mutex_init(&pool->mutex, NULL);

    for (int i = 0; i < pool->size; i++)
    {
        PRT("work thread [%02d] creating !\n", i);
        pool->thread[i].id = i;
        thrd[i].pool = pool;
        thrd[i].thid = i;
        (void)pthread_create(&pool->thread[i].threads, NULL, thread_pool_worker, (void *)&thrd[i]);
        PRT("work thread [%02d] created success !\n", i);
        usleep(1);
    }
    usleep(10000);
    free(thrd);
    return pool;
}

int thread_pool_add_task(THREAD_POOL *pool, THRD_FUNC func, void *arg)
{
    if (NULL == pool || NULL == arg)
    {
        return -1;
    }

    volatile static unsigned int id = 0;
    int cnt = 0;
    id++;
    while (TASK_MAX_SIZE <= pool->task_count)
    {
        cnt += 1;
        usleep(100);
        if (cnt >= 10)
        {
            goto ERR;
        }
        continue;
    }

    pthread_mutex_lock(&pool->mutex);

    if (0 == push(&pool->taskQ, func, arg, id))
    {
        pool->task_count++;
    }
    else
    {
        pthread_mutex_unlock(&pool->mutex);
        goto ERR;
    }

    pthread_cond_signal(&pool->cond);
    pthread_mutex_unlock(&pool->mutex);

    return 0;

ERR:
    return -1;
}

void thread_pool_destroy(THREAD_POOL *pool)
{
    if (NULL == pool)
    {
        return;
    }

    unsigned long int totalTask = 0;
    pthread_mutex_lock(&pool->mutex);
    pool->state = 0;                     // 将线程池状态设置为停止
    pthread_cond_broadcast(&pool->cond); // 唤醒所有等待的线程
    pthread_mutex_unlock(&pool->mutex);
    POK("-------------------------------------\n");

    for (int i = 0; i < pool->size; i++)
    {
        // 等待所有线程执行完毕
        pthread_join(pool->thread[i].threads, NULL);
        totalTask += pool->thread[i].cnt;
        POK("[%02d]thread[%02ld] exec Task cnt : [%03ld]\n", i, pool->thread[i].id, pool->thread[i].cnt);
    }

    POK("Statistics: Thread Pool Size[%lu] All Task[%lu]\n", pool->size, totalTask);
    POK("-------------------------------------\n");

    pthread_mutex_destroy(&pool->mutex);
    pthread_cond_destroy(&pool->cond);
    free(pool);
}

void prt(int arg)
{
    thprint("TASK :: [%d]\n", arg);
    usleep(1000);
    return;
}

void prtq(TASKQ *q)
{
    if (NULL == q)
    {
        return;
    }

    for (int i = 0; i < TASK_QUEUE_MAX_SIZE; i++)
    {
        DEB("[%d]: id[%d] -- h[%d], r[%d]\n", i, q->task[i].id, q->head, q->rear);
    }
    return;
}

int test_main()
{
    THREAD_POOL *pool = thread_pool_init(THREAD_POOL_SIZE);
    usleep(100000);
    for (int i = 0; i < 20; i++)
    {
        (void)thread_pool_add_task(pool, (THRD_FUNC)prt, (void *)(long)i);
    }

    thread_pool_destroy(pool);

    return 0;
}

#define YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 + (__DATE__[9] - '0')) * 10 + (__DATE__[10] - '0'))

#define MONTH ((__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')   ? 1  \
               : (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b') ? 2  \
               : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') ? 3  \
               : (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r') ? 4  \
               : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') ? 5  \
               : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') ? 6  \
               : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') ? 7  \
               : (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g') ? 8  \
               : (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p') ? 9  \
               : (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't') ? 10 \
               : (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v') ? 11 \
                                                                                  : 12)

#define DAY ((__DATE__[4] == ' ' ? 0 : __DATE__[4] - '0') * 10 + (__DATE__[5] - '0'))

void printfThreadPoolVersion()
{
#ifdef DEBUG
    thprint("ThreadPool Version: V%d.%d.%d build %04d%02d%02d %s Debug\n", MAJOR_VERSION, MINOR_VERSION, REPAIR_VERSION, YEAR, MONTH, DAY, __TIME__);
    thprint("THREAD_POOL:\t %lu\n", sizeof(THREAD_POOL));
    thprint("TASK:\t %8lu\n", sizeof(TASK));
    thprint("TASKQ:\t %8lu\n", sizeof(TASKQ));
    thprint("THREAD:\t %8lu\n", sizeof(THREAD));
    thprint("THRD:\t %8lu\n", sizeof(THRD));
#else
    thprint("ThreadPool Version: V%d.%d.%d build %04d%02d%02d %s Release\n", MAJOR_VERSION, MINOR_VERSION, REPAIR_VERSION, YEAR, MONTH, DAY, __TIME__);
#endif
}